#-------------------------------------------------------------------------------
# Name:        AI
# Purpose:
#
# Author:      Paul
#
# Created:     13/03/2012
# Copyright:   (c) Paul 2012
# Licence:     <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python
from connect4board import *
from random import random, randint
import copy
import time

DEPLIM = 4 # Depth limit for minimax search

def miniMaxDecision(gameBoard, currentPlayer):
    """Evaluate which column (children) to pick and return int from 0 to 6 for the slot
    @gameBoard the game state/board
    @currentPlayer - 'X' or 'O' to be played
    """
    #Evaluate children for the slots that are legal
    legality = legalMoves(gameBoard)

    #switch players
    nextPlayer = otherPlayer(currentPlayer)

    #if move is legal, deep copy and play move on the new deep copied board
    children = {} # {0: [gameBoard, value], 2: [gameBoard, value] ... etc }
    #dict of gameboards and value. If state is not reachable, it's not here
    #look up 7 slots, 0 to 6, pull list of two values: [board state,value]

    temp = [0,1,2,3,4,5,6] #preallocate list with ints
    for i in legality:
        temp[i] = copy.deepcopy(gameBoard)
        temp[i].playMove(currentPlayer, i) #play move on the child board
        children[i] = [temp[i], miniMaxValue(temp[i], nextPlayer, 'min', 1)] #Find the value of each child, dict of {slot: [board,value]} , Pass depth of 1

    #take the maximum value move (called child or branch)
    bestval, bestslot, beststate = max([(v,k,s) for (k,(s,v)) in children.iteritems()])
    print children
    print bestval, bestslot, beststate

    #return int slot 0 to 6
    return bestslot

def miniMaxValue(gameBoard, currentPlayer, minormax, depth):
    """
    @gameBoard current board state
    @currentPlayer 'X' or 'O' to play. It's currentPlayer's turn to play on the gameBoard now
    @minormax 'min' or 'max'.
    @depth root decision depth was 0

    Returns a utlity value of current position on gameBoard between -1000 and 1000
    uses minimax and then heuristic after a certain depth"""

    #switch player and min/max
    nextPlayer = otherPlayer(currentPlayer)
    nextminormax = otherminormax(minormax)

    if terminalTest(gameBoard) == True:
        return heuristic(gameBoard, currentPlayer, minormax)

    if depth == DEPLIM:
        return heuristic(gameBoard, currentPlayer, minormax)

    legality = legalMoves(gameBoard)
    children = {}
    temp = [0,1,2,3,4,5,6] #preallocate list with ints

    #call min / max
    if minormax == 'max':
        for i in legality:
            temp[i] = copy.deepcopy(gameBoard)
            temp[i].playMove(currentPlayer, i) #play move on the child board
            children[i] = [temp[i], miniMaxValue(temp[i], nextPlayer, 'min', depth + 1)] #Find the value of each child, dict of {slot: [board,value]} , Pass depth of 1

        #take the maximum value move (called child or branch)
        bestval, bestslot, beststate = max([(v,k,s) for (k,(s,v)) in children.iteritems()])
        return bestval

    if minormax == 'min':
        for i in legality:
            temp[i] = copy.deepcopy(gameBoard)
            temp[i].playMove(currentPlayer, i) #play move on the child board
            children[i] = [temp[i], miniMaxValue(temp[i], nextPlayer, 'max', depth + 1)] #Find the value of each child, dict of {slot: [board,value]} , Pass depth of 1

        #take the minimum value move (called child or branch)
        lowestval, lowestslot, loweststate = min([(v,k,s) for (k,(s,v)) in children.iteritems()])
        return lowestval

def legalMoves(gameBoard):
    """return list of legal column choices such as [0,1,3,6]
    contains the numbers 0 through 6 in order, each column at most once"""
    legality = []
    for i in range(7):
        if gameBoard.board[0][i] == empty:
            legality.append(i)
    return legality

def otherPlayer(currentPlayer):
    """If 'X' is input, output 'O',
    if 'O' is input, output 'X'"""
    if currentPlayer == 'X':
        return 'O'
    if currentPlayer == 'O':
        return 'X'

def otherminormax(minormax):
    """If 'min' is input, output 'max',
    if 'max' is input, output 'min'"""
    if minormax == 'min':
        return 'max'
    if minormax == 'max':
        return 'min'

def terminalTest(gameBoard):
    """check if a player has won or game is filled, return True if terminal, False if not"""
    winner = gameBoard.winCheck() # returns 'X', 'O', 'D', or 'N'
    if winner == 'X' or winner == 'O' or winner == 'D':
        return True
    if winner == 'N':
        return False

def Move(gameBoard, currentPlayer):
    """inputs: a current player and gameboard state
    outputs: column to move to 0-6. Only returns a legal move.
    """
    return miniMaxDecision(gameBoard,currentPlayer)

def simpleMove(gameBoard):
    """Randomly choose a legal (column check) move 0 - 6"""

    while(1): #loop until legal move found
        r = randint(0,6) #random number 0 through 6 inclusive

        #Check if it's legal by checking whether column has been filled
        if gameBoard.board[0][r] == empty:
            return r

def whatisAItoken(currentPlayer,minormax):
    """@currentPlayer 'X' or 'O'
    @minormax 'min' or 'max'
    returns (aitoken, opponenttoken) eg ('O', 'X')
    """
    if (currentPlayer == 'X' and minormax == 'max') or (currentPlayer == 'O' and minormax == 'min'):
        return ('X', 'O')
    if (currentPlayer == 'O' and minormax == 'max') or (currentPlayer == 'X' and minormax == 'min'):
        return ('O', 'X')

def heuristic(gameBoard, currentPlayer, minormax):
    """@AItoken 'X' or 'O' - Which player is the AI?
    @gameBoard current position
    outputs: integer value (ranking from -1000 to 1000) of position
    """
    #setup other player to opposite token
    (AItoken, otherPlayer) = whatisAItoken(currentPlayer, minormax)

    winner = gameBoard.winCheck()
    if winner == AItoken:
        return 1000
    if winner == otherPlayer:
        return -1000

    return randint(-100,100) #return random heuristic int between -100 and 100 for testing

def testFill1(gameBoard):
    """Fill the board for testing purposes"""
    #Fill a column for testing purposes
    gameBoard.board[0][0] = 'X'
    gameBoard.board[1][0] = 'O'
    gameBoard.board[2][0] = 'X'
    gameBoard.board[3][0] = 'O'
    gameBoard.board[4][0] = 'X'
    gameBoard.board[5][0] = 'O'

    #2nd column

    gameBoard.board[5][1] = 'O'

    #Partially fill 3rd column
    gameBoard.board[1][2] = 'X'
    gameBoard.board[2][2] = 'X'
    gameBoard.board[3][2] = 'X'
    gameBoard.board[4][2] = 'O'
    gameBoard.board[5][2] = 'O'


def main():
    #module testing code below
    gameBoard = Board()
    currentTime = time.time()

    print miniMaxDecision(gameBoard, 'X')
    split = time.time() - currentTime
    print "time taken: ", split

    gameBoard.printBoard()
if __name__ == '__main__':
    main()
